package main

import (
	"fmt"
	"knowledge-go/cpt/c_000_com"
	"reflect"
)

/*
Go语言中，反射的机制就是在运行的时候，可以获取到其变量的类型和值，且可以对其类型和值进行检查，对其值进行修改。
即在不知道具体的类型的情况下，可以用反射机制来查看变量类型、更新变量的值。
Go中反射主要涉及到两个概念：Type和Value。对所有的接口进行反射时，都可以得到一个包含Type和Value的信息结构，
Type是反射的这个变量本身的类型信息，Value是反射的这个变量本身的值信息。
*/
func main() {
	user := c_000_com.User{Id: 1, Name: "张三"}

	//1.获得变量类型
	var t reflect.Type = reflect.TypeOf(user)
	fmt.Println("1.获得变量类型", t.String()) //c_000_com.User

	// 2.获得类型的常量值
	var kind reflect.Kind = t.Kind()
	fmt.Printf("2.获得类型的常量值, kind=%s, 类型是否为Struct=%v \n", kind, kind == reflect.Struct)

	// 3.获得变量值
	var v reflect.Value = reflect.ValueOf(user)
	fmt.Println("3.获得变量值", v)

	// 4.修改变量值
	// 通过反射获得变量的内存地址，并获得Elem
	var elem reflect.Value = reflect.ValueOf(&user).Elem()
	// 判断是否允许修改
	if elem.CanSet() {
		name := elem.FieldByName("Name")
		name.SetString("李四")
		fmt.Println("4.修改变量值，修改后", user)
	}

	// 5.修改变量值，修改传入完整的类型
	newUser := reflect.ValueOf(c_000_com.User{Name: "666"})
	elem.Set(newUser)
	fmt.Println("5.修改变量值，直接赋值,修改后", user)

	// 6.通过指针，获得内存地址的对数
	var x reflect.Value = reflect.ValueOf(&user).Elem()
	fmt.Println("6.通过指针，获得内存地址的对数", x)

	// 7.获得所有方法【总结：获得信息用TypeOf】
	rType := reflect.TypeOf(user)
	pType := reflect.TypeOf(&user)
	fmt.Println("7.方法的数量", rType.NumMethod(), pType.NumMethod())
	for i := 0; i < rType.NumMethod(); i++ {
		fmt.Println("7.rType:", i, ", method=", rType.Method(i))
	}
	for i := 0; i < pType.NumMethod(); i++ {
		fmt.Println("7.pType:", i, ", method=", pType.Method(i))
	}

	// 8.调用方法【总结：操作对象用ValueOf】
	rValue := reflect.ValueOf(user)
	method := rValue.MethodByName("GetUserName")
	// 调用方法
	res := method.Call(nil)
	// 获取方法的返回值
	userName := res[0].String()
	fmt.Println("8.调用方法", userName)

}
